package com.gyl.aspect.beanverify;


import com.gyl.aspect.annotation.FieldIntegerValueLimited;
import com.gyl.aspect.annotation.FieldLengthLimited;
import com.gyl.aspect.annotation.FieldNotNull;
import com.gyl.aspect.annotation.FieldNotNullDepends;
import com.gyl.aspect.annotation.FieldNotNullDependsContainer;
import com.gyl.aspect.annotation.FieldNotNullIntDepends;
import com.gyl.aspect.annotation.FieldPatternLimited;
import com.gyl.aspect.annotation.FieldValueLimited;
import com.gyl.aspect.annotation.FieldsNotEitherNull;
import com.gyl.aspect.annotation.NumberFieldValueLimited;
import com.gyl.aspect.utils.ReflectUtil;
import com.gyl.common.bean.exception.BusinessException;
import com.gyl.common.utils.StringUtil;

import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;


/**
 * @author: gyl
 */
@Component
public class BeanVerify {
	/**
	 * 缓存的, 用来做字符串格式匹配的Pattern, 以正则表达式做key Pattern仅为工具类, 调用途中不会改变Pattern自己, 线程安全
	 */
	private Map<String, Pattern> patternMaps = new HashMap();


	/**
	 * 检测一个List列表里的所有对象是否符合其上边添加的注解的要求.
	 * @param productList
	 * @author gyl @date 2019/03/11
	 * @throws Exception
	 */
//	@LogMethodProcess(methodName = "请求参数中商品列表的合法性校验")
	private <T> void veriryProductList(List<T> productList) throws Exception {
		if (null == productList || productList.isEmpty()) {
			return;
		}
		for (T product : productList) {
			Verify(product);
		}
	}


	/**
	 * 根据请求实体和其字段上的注解, 对请求实体的各字段进行验证, 如有不符合注解要求的,根据注解中的错误信息抛出异常. 验证的范围: 包括该类自己声明的字段,
	 * 自父类继承来的字段 只能验证该对应本身的字段, 不能验证字段的字段(当字段为Object类型的时候)
	 * 
	 * @param obj
	 * @throws Exception
	 */
	public void Verify(Object obj) throws Exception {
		Class clazz = obj.getClass();
		// 验证该对象自己声明的,自父类继承的属性是否符合注解,直到Object为止(不包括Object)
		while (!clazz.equals(Object.class)) {
			verifyItself(clazz, obj);
			// 依次拿到该类对应的父类.
			clazz = clazz.getSuperclass();
		}
	}

	/**
	 * 工具方法: 检测一个对象的各字段是否符合其上边注解的限制. 若不符合,则按注解中指定的错误信息报出异常
	 * 
	 * @param clazz
	 * @param obj
	 * @throws Exception
	 * @throws SecurityException
	 */
	private void verifyItself(Class clazz, Object obj) throws Exception {

		// 一个对象的几个字段不允许全部为空, 这是类上的注解.
		FieldsNotEitherNull fieldsNotEitherNull = (FieldsNotEitherNull) clazz
				.getDeclaredAnnotation(FieldsNotEitherNull.class);
		verifyFieldsNotEitherNull(fieldsNotEitherNull, obj, clazz);

		// 拿到该类自己声明的所有属性
		Field[] fields = clazz.getDeclaredFields();

		for (Field item : fields) {

			// 以Object形式拿到该字段的值, 可能为空指针
			Object fieldValueAsObject = ReflectUtil.getFieldValue(item,obj);

			// 必须先做空值处理! 否则可能会出现空指针异常. 空值(空指针,空字符串,"null"字符串)处理
			FieldNotNull notNullLimit = item.getDeclaredAnnotation(FieldNotNull.class);
			if (null != notNullLimit && StringUtil.isNullOrEmptyAfterTrim(fieldValueAsObject)) {
				// 被注解修饰,该字段又为空,则按照注解里指定的错误信息抛出异常
				throw new BusinessException(notNullLimit.nullError(), notNullLimit.subErrMsg());
			}

			// 当某一个字段为某值时,该字段不允许为空--条件有多个
			FieldNotNullDependsContainer notNullByPres = item.getDeclaredAnnotation(FieldNotNullDependsContainer.class);
			if (null != notNullByPres) {
				for (FieldNotNullDepends limit : notNullByPres.previousLimiteds()) {
					verifyFieldNotNullByPreviousFieldValue(limit, clazz, obj, fieldValueAsObject);
				}
			}

			// 当某一个字段为某值时,该字段不允许为空--条件只有一个（String）
			FieldNotNullDepends notNullByPre = item.getDeclaredAnnotation(FieldNotNullDepends.class);
			verifyFieldNotNullByPreviousFieldValue(notNullByPre, clazz, obj, fieldValueAsObject);

			// 当某一个字段为某值时,该字段不允许为空--条件只有一个（int）
			FieldNotNullIntDepends notNullIntByPre = item.getDeclaredAnnotation(FieldNotNullIntDepends.class);
			verifyFieldNotNullIntByPreviousFieldValue(notNullIntByPre, clazz, obj, fieldValueAsObject);

			// 如果该字段不是必填, 且为空, 就不再做任何校验
			if (null == fieldValueAsObject || "".equals(fieldValueAsObject)) {
				continue;
			}

			// 检验list类型的每个实体的属性
			if (fieldValueAsObject instanceof List) {
				List<Object> list = (List<Object>) fieldValueAsObject;
				veriryProductList(list);
			}

			// 长度处理
			FieldLengthLimited lengthLimit = item.getDeclaredAnnotation(FieldLengthLimited.class);
			verifyFieldLengthLimited(lengthLimit, fieldValueAsObject);

			// 字段值不合法(不在给定的字段值集合内)处理
			// 取到字段值限制为某一个集合内的注解
			// String类型
			FieldValueLimited valueLimit = item.getDeclaredAnnotation(FieldValueLimited.class);
			verifyFieldValueLimited(valueLimit, fieldValueAsObject);
            // int类型
			FieldIntegerValueLimited valueIntLimit = item.getDeclaredAnnotation(FieldIntegerValueLimited.class);
			verifyFieldIntValueLimited(valueIntLimit, fieldValueAsObject);

			// 判断字段是否符合既定的格式
			FieldPatternLimited patternLimited = item.getDeclaredAnnotation(FieldPatternLimited.class);
			verifyFieldPatternLimited(patternLimited, fieldValueAsObject);

			// 判断数字型的字段大小是否符合规范
			NumberFieldValueLimited numberValueLimited = item.getDeclaredAnnotation(NumberFieldValueLimited.class);
			verifyNumberFieldValueLimited(numberValueLimited, fieldValueAsObject);
		}
	}

	/**
	 * @description 校验数字范围大小
	 * @author gyl
	 */
	private void verifyNumberFieldValueLimited(NumberFieldValueLimited numberValueLimited, Object fieldValueAsObject) {
		// 没有注解, 或字段为空,或字段不是数字类型, 则不校验
		if (StringUtils.isEmpty(numberValueLimited)) {
			return;
		}
		Number number = null;
		if(fieldValueAsObject instanceof String){
			if(((String) fieldValueAsObject).length() <= 9){
				number = Integer.valueOf(fieldValueAsObject.toString());
			}else{
				throw new BusinessException(numberValueLimited.overMaxValueError(), numberValueLimited.tooBigSubErrMsg());
			}
		}else{
			// 既然被该注解修饰, 就肯定是数字类型
			number = (Number) fieldValueAsObject;
		}
		int minValue = numberValueLimited.minValue(), maxValue = numberValueLimited.maxValue();
		// 指定了最小值, 又小于最小值
		if (-1 != minValue && number.intValue() < minValue) {
			throw new BusinessException(numberValueLimited.lessThanMinValueError(), numberValueLimited.tooSmallSubErrMsg());
		}
		// 指定了最大值, 又大于最大值
		if (-1 != maxValue && number.intValue() > maxValue) {
			throw new BusinessException(numberValueLimited.overMaxValueError(), numberValueLimited.tooBigSubErrMsg());
		}
	}

	/**
	 * @description 字符串字段，校验格式
	 * @author gyl Shang @date 2020-04-02 18:09:47
	 */
	private void verifyFieldPatternLimited(FieldPatternLimited patternLimited, Object fieldValueAsObject) {
		// 如果注解为空, 即没有注解, 或字段为空, 就不验证
		if (null == patternLimited) {
			return;
		}
		String fieldValue = String.valueOf(fieldValueAsObject);
			String key = patternLimited.pattern();
		Pattern pattern = patternMaps.get(key);
		if (null == pattern) {
			pattern = Pattern.compile(key);
			patternMaps.put(key, pattern);
		}
		// 如果不符合正则表达式的要求, 就按照约定抛出异常
		if (!pattern.matcher(fieldValue).matches()) {
			throw new BusinessException(patternLimited.notMatchError(), patternLimited.subErrMsg());
		}
	}

	/**
	 * @description 前置字段条件下，不允许为空(String)
	 * @author gyl
	 */
	private void verifyFieldNotNullByPreviousFieldValue(FieldNotNullDepends notNullByPre, Class clazz, Object obj,
                                                        Object fieldValueAsObject) throws Exception {
		// 没有被注解修饰
		if (null == notNullByPre) {
			return;
		}
		// 拿到前限定字段的值--字符串
		String preFieldValue = (String) ReflectUtil.getFieldValue(clazz, obj, notNullByPre.dependsFieldName());

		if (preFieldValue.trim().equals(notNullByPre.dependsFieldValue())
				&& StringUtil.isNullOrEmptyAfterTrim(fieldValueAsObject)) {
			// 前限定字段的值符合前限定条件,而该字段又为空,则根据枚举中设置的错误信息报出异常
			throw new BusinessException(notNullByPre.nullError(), notNullByPre.subErrMsg());
		}
	}

	/**
	 * @description 前置字段条件下，不允许为空(String)
	 * @author gyl
	 */
	private void verifyFieldNotNullIntByPreviousFieldValue(FieldNotNullIntDepends notNullByPre, Class clazz, Object obj,
														Object fieldValueAsObject) throws Exception {
		// 没有被注解修饰
		if (null == notNullByPre) {
			return;
		}
		// 拿到前限定字段的值--字符串

		Integer preFieldValue =  (Integer) ReflectUtil.getFieldValue(clazz, obj, String.valueOf(notNullByPre.dependsFieldName()));

		if (preFieldValue.equals(notNullByPre.dependsFieldValue())
				&& StringUtil.isNullOrEmptyAfterTrim(fieldValueAsObject)) {
			// 前限定字段的值符合前限定条件,而该字段又为空,则根据枚举中设置的错误信息报出异常
			throw new BusinessException(notNullByPre.nullError(), notNullByPre.subErrMsg());
		}
	}

	/**
	 * @description 字符串长度校验
	 * @author gyl
	 */
	private void verifyFieldLengthLimited(FieldLengthLimited lengthLimit, Object fieldValueAsObject) throws Exception {
		// 如果没有注解,或者字段为空,则不校验. 是否为空的检验使用了另一个注解.
		if (null == lengthLimit) {
			return;
		}
		String fieldValue = String.valueOf(fieldValueAsObject);
		// 取到该字符串的长度
		int fieldLength = fieldValue.length();

		// 注解中指定了准确的字符段长度
		if (lengthLimit.length() > 0 && fieldLength != lengthLimit.length()) {
			// 字段的长度不等于指定的长度
			throw new BusinessException(lengthLimit.shorterOrLongerError(), lengthLimit.shorterOrLongerSubErrMsg());
		}
		// 注解中指定了最小长度,但是实际长度又小于最小长度
		if (lengthLimit.minLength() > 0 && fieldLength < lengthLimit.minLength()) {
			throw new BusinessException(lengthLimit.tooShortError(), lengthLimit.tooShortSubErrMsg());
		}

		// 指定了最大长度, 但是实际长度又大于最大长度
		if (lengthLimit.maxLength() > 0 && fieldLength > lengthLimit.maxLength()) {
			// 长度大于指定的最大长度
			throw new BusinessException(lengthLimit.tooLongError(), lengthLimit.tooLongSubErrMsg());
		}
	}

	/**
	 * @description 枚举值校验
	 * @author gyl
	 */
	public void verifyFieldValueLimited(FieldValueLimited valueLimited, Object fieldValueAsObject)
			throws IllegalArgumentException, IllegalAccessException {
		// 如果没有该注解,或者该字段为空,就不校验
		if (null == valueLimited) {
			return;
		}
		String fieldValue = String.valueOf(fieldValueAsObject);
		// 如果在指定的值范围内,就放过
		for (String item : valueLimited.limitedValues()) {
			if (item.equals(fieldValue)) {
				return;
			}
		}
		// 否则就抛出异常
		throw new BusinessException(valueLimited.overLimitedValueError(),valueLimited.subErrMsg());
	}

	/**
	 * @description 枚举值校验
	 * @author gyl
	 */
	public void verifyFieldIntValueLimited(FieldIntegerValueLimited valueLimited, Object fieldValueAsObject)
			throws IllegalArgumentException {
		// 如果没有该注解,或者该字段为空,就不校验
		if (null == valueLimited) {
			return;
		}
		int fieldValue = Integer.valueOf(String.valueOf(fieldValueAsObject));
		// 如果在指定的值范围内,就放过
		for (int item : valueLimited.limitedValues()) {
			if (item == fieldValue) {
				return;
			}
		}
		// 否则就抛出异常
		throw new BusinessException(valueLimited.overLimitedValueError(),valueLimited.subErrMsg());
	}

	/**
	 * @description 入参多个字段不能同时为空
	 * @author gyl
	 */
	public void verifyFieldsNotEitherNull(FieldsNotEitherNull fieldsNotEitherNull, Object obj, Class clazz)
			throws Exception {
		if (null == fieldsNotEitherNull) {
			return;
		}
		String[] fieldNames = fieldsNotEitherNull.fieldNames();
		// 遍历属性名,依次取出对应的值,只要有一个不为空校验即通过
		for (String fieldName : fieldNames) {
			Object fieldValue = ReflectUtil.getFieldValue(clazz, obj, fieldName);
			if (!StringUtil.isNullOrEmptyAfterTrim(fieldValue)) {
				// 只要有一个字段不为空, 校验即通过
				return;
			}
		}
		// 通过for循环,说明不能为空的字段全部为空了, 报出错误信息
		throw new BusinessException(fieldsNotEitherNull.bothNullError());

	}
}